#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <math.h>

#include "main.h"
#include "sim_init.h" 
#include "token_arb.h" 
#include "osf.h"
#include "inj_ejt.h"
#include "power.h"

#define MOD_INC(T) (T + 1) % CHN_FLY_TIME

void sim_result(void);
void dump_jitters(void); 

int main(int argc, char* argv[])
{
  struct timeval start, end; 
  /* 1. Initialize the network 
   *    a. Initialize NUM_NODES nodes, each node has NUM_CHN injection queues 
   *    (except when NUM_CHN = 1)
   *    (Also determines the reservation allocated to each node)  
   *    b. Initialize NUM_CHN token rings. On a ring *i*, node *i* is the 
   *    home node, which has one ejection queue. 
   *    c. For each channel, a request vector set by requesting nodes, and a 
   *    token exist flag set by the home node
   *    d. For each home node, a queue occupation vector with size as large as the 
   *    time-of-flight on the waveguide is needed to record arrival of flits. This 
   *    is used to track real-time occupation of the buffer space. 
   *    e. For each home node, a done flag and done_clock counter is used to trigger 
   *    frame-switching 
   *    f. Finally, frame-switching is broadcast to all nodes
   */ 
  sim_init(argc, argv); 
  printf("Intialization complete\n");

  SIM_END_FLAG = false;
  /* 2. Interconnection Network Pipeline (pipeline is reversed) 
   *    For each source clock cycle: 
   *      a. If a ready flit exist, each sender generates requests; 
   *         otherwise, the sender increments the a *completion* counter
   *      b. The home node checks its buffer occupation with the queue 
   *         occupation vector, and determines whether to assert a token.
   *         
   *         The home node also eject a flit from the ejection queue, according 
   *         to its real-time capacity. 
   *
   *      c. For the channel where a token is asserted, carry out arbitration.
   *      d. Winners from arbitration release one ready flit and update corresponding 
   *         queue occupation vector. 
   *      e. When the completion counter is equal to NUM_NODES-1, reset it and 
   *         schedule a frame-switching (by broadcast) in the future. 
   *      f. When receive the scheduled frame-switching, each node switches frames. 
   *      g. Each node push a flit into the source queue, according to a random 
   *         number generator.
   */ 
  gettimeofday(&start, NULL);  
  while(!SIM_END_FLAG)
  {  
    /* 1. Request and arbitration (abcd)*/ 
    token_arb_stage();
    /* 2. Frame switching (ef) */
    if(QOS)
      osf_stage();
    /* 3. Traffic generation (g) */
    //for(int i = 0;i < 4;i ++)  
    inj_flit_stage();
    /* 4. Ejection */ 
    ejt_flit_stage(); 

    SRC_CLK ++;
    DLY_TAP_PT = MOD_INC(DLY_TAP_PT);
    
    /* stat */
    if(SRC_CLK % STAT_INTV == 0)
    {
      average_flit_latency = total_flit_latency / num_ejt_flits;
      accepted_throughput  = (double) num_ejt_flits / SRC_CLK;  
      printf("[%d|%d|%d]---[%.4f|%.4f]\n", SRC_CLK, num_inj_flits, 
             num_ejt_flits, average_flit_latency, accepted_throughput);
    } 
    if(SRC_CLK % (10 * STAT_INTV) == 0) 
      sim_result();

    if(num_inj_flits >= NUM_FLT_TO_INJ && num_ejt_flits == num_inj_flits)
      SIM_END_FLAG = true; 
  }  

  gettimeofday(&end, NULL); 
  printf("Simualtion finishes, using %d secs\n", end.tv_sec - start.tv_sec);
  printf("Final statistics:\n");  
  sim_result();
  dump_jitters();  
  return 0; 
}

void sim_result(void)
{
  int node;
  double total_energy; 
  if(QOS)
  {
    laser_power = (double) PS_LP_QOS * SRC_CLK; 
    ring_heating_power = (double) PS_HR_QOS * SRC_CLK; 
  }
  else 
  {
    laser_power = (double) PS_LP * SRC_CLK; 
    ring_heating_power = (double) PS_HR * SRC_CLK; 
  }
   
  total_energy = dyn_o_power + dyn_e_power + laser_power + ring_heating_power;  

  printf("[%d]=======================\n", SRC_CLK/1000); 
  average_flit_latency = total_flit_latency / num_ejt_flits; 
  accepted_throughput  = (double) num_ejt_flits / SRC_CLK; 
  
  printf("Elapsed time:                        %d\n", SRC_CLK);  
  printf("Injected/ejected flits:              [%d/%d]\n", num_inj_flits, num_ejt_flits); 
  printf("Average flit latency (cycle):        %.4f\n", average_flit_latency);
  printf("Throughput:                          %.4f\n", accepted_throughput);
  printf("Total energy                         %.4f nJ\n", total_energy/1000000);
  printf("Laser energy                         %.4f nJ\n", laser_power/1000000);
  printf("Ring heating                         %.4f nJ\n", ring_heating_power/1000000);
  printf("Dynanmic optical energy              %.4f nJ\n", dyn_o_power/1000000);
  printf("Dynanmic electrical energy           %.4f nJ\n", dyn_e_power/1000000);
  printf("Average flit jitter                  %d\n", tot_jitter/num_jitter);
  printf("Stddev. of flit jitter               %d\n", (int)sqrt(tot_jitter_var/num_jitter));
//  printf("===========================\n"); 
//  printf("Detailed node throughputs:\n");
//  for(node = 0;node < NUM_NODES;node ++)
//    printf("node:%d throu:                   %.4f\n", node, (double) node_throughputs[node] / (double) SRC_CLK); 
}
void dump_jitters(void)
{
  FILE *fp;
  fp = fopen("jitters.txt", "w");
  for(int i = 0;i < 100;i ++) 
    fprintf(fp, "%d ", bin_jitter[i]); 
  fclose(fp); 
} 
